; https://blog.51cto.com/u_15711993/5455278

LOADER_START_SECTOR equ 0x1

SECTION MBR vstart=0x7c00         
mov ax,0
mov ss,ax
mov sp,0x7c00

mov dx,0x1f7     ; 0x1f7=primary channel's status
.not_ready1:
    nop              ; 只是为了增加延迟
    in al,dx
    and al,0xc0      ; 0xc0=1100_0000b取bit 6~7
    cmp al,0x40      ; 检查bit 6, 设备是否就绪
    jnz .not_ready1  ;若未准备好，继续等

;设置将要操作的扇区个数是2
mov ax, 2        
mov dx,0x1f2
out dx,al

;设置将要从扇区LOADER_START_SECTOR开始读数据，注意0x1f6端口，设置主硬盘，LBA方式
mov eax,LOADER_START_SECTOR

;LBA地址7~0位写入端口0x1f3
mov dx,0x1f3      ;   0x1f3=primary channel's lba low        
out dx,al

;LBA地址15~8位写入端口0x1f4
shr eax,8         ;   eax值右移8位
mov dx,0x1f4      ;   0x1f4=primary channel's lba mid
out dx,al

;LBA地址23~16位写入端口0x1f5
shr eax,8
mov dx,0x1f5      ;   0x1f5=primary channel's lba high
out dx,al

; 设置device端口
shr eax,8
and al,0x0f      ; lba第24~27位, 其他bit置为0
or al,0xe0       ; 设置7～4位为1110,表示lba模式, 并使用主盘
mov dx,0x1f6     ; 0x1f6=primary channel's device
out dx,al

;设置要执行的操作是读操作
mov ax,0x20
mov dx,0x1f7
out dx,al        ;开始读

mov di,2

NextSector:
waits:
  mov dx,0x1f7
  in  al,dx
  and al,0x88        ;取位7和位3
  cmp al,0x08      ;如果位7=0则表示不繁忙，位3=1则表示可以读取数据
  jne waits
  
mov cx,256D        ;准备循环256次，因为1f0是16位端口，所以每次读取
            ;2字节，一共读取512字节，正好1个扇区大小
mov ax,0xb800      ;设置ds段寄存器b800，这是显存起始位置
mov ds,ax

mov dx,0x1f0      ;从1f0端口读数据，放到ax中，in指令只能放到ax中
              ;注意1f0是16位端口而非8位
read:
  in  ax,dx        ;此时要将AX拆分成4块，分别显示asicc
  ;准备显示数据，因为nasm编译后在硬盘是小端法存储，所以先显示低字节，后显示高字节
  
  ; 这里删除了打印第一个字节高位的逻辑

  ;处理低4位
  mov bx,ax        
  and bx,0x0f        ;获取低4位
  
  cmp bl,0x0A        ;与10比较
  jb   show_number4    ;如果是0-9则显示数字，A-F则显示字母
    add bl,55D      ;显示字母，则内存中数字需要+55  
    jmp show_end4
  show_number4:      ;显示数字，则内存中数字需要+48
    add bl,48D      
  show_end4:
    call do_show
  
  ;call show_space      ;显示1个字节之后，空格隔开
  
  ; 这里删除了打印第2个字节高位的逻辑
 
  ;处理次高4位
  mov bx,ax        
  and bx,0x0f00      ;获取次高4位
  shr bx,0x8        ;右移8位，例如00001111 00000000->1111
              ;此时bx次高4位的值已经跑到了bl低4位中
  cmp bl,0x0A        ;与10比较
  jb   show_number2    ;如果是0-9则显示数字，A-F则显示字母
    add bl,55D      ;显示字母，则内存中数字需要+55  
    jmp show_end2
  show_number2:      ;显示数字，则内存中数字需要+48
    add bl,48D      
  show_end2:
    call do_show
  
  ;call show_space      ;显示1个字节之后，空格隔开
  loop read
  dec di
  cmp di,0
  jnz NextSector
show_space:
  mov byte [si],' '    ;空格隔开更好看
  inc si
  mov byte [si],0x0b
  inc si
  ret
do_show:
  mov byte [si],bl    ;显示字母或数字
  inc si
  mov byte [si],0x0b
  inc si
  ret

notstop: jmp notstop

times 510-($-$$) db 0
db 0x55,0xaa